package io.coderyeah.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.coderyeah.basic.Result;
import io.coderyeah.basic.dto.LoginDto;
import io.coderyeah.basic.exception.BusinessException;
import io.coderyeah.basic.jwt.JwtUtils;
import io.coderyeah.basic.jwt.LoginData;
import io.coderyeah.basic.jwt.RsaUtils;
import io.coderyeah.basic.util.HttpUtil;
import io.coderyeah.org.mapper.EmployeeMapper;
import io.coderyeah.system.domain.Menu;
import io.coderyeah.user.constant.VerifyCodeConstants;
import io.coderyeah.user.constant.WxConstants;
import io.coderyeah.user.domain.LoginInfo;
import io.coderyeah.user.domain.User;
import io.coderyeah.user.domain.WxUser;
import io.coderyeah.user.dto.BinderDTO;
import io.coderyeah.user.mapper.LoginInfoMapper;
import io.coderyeah.user.mapper.UserMapper;
import io.coderyeah.user.mapper.WxUserMapper;
import io.coderyeah.user.service.LoginInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class LoginInfoServiceImpl extends ServiceImpl<LoginInfoMapper, LoginInfo> implements LoginInfoService {
    @Autowired
    private LoginInfoMapper loginInfoMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private WxUserMapper wxUserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private EmployeeMapper employeeMapper;


    // 账户登录
    @Override
    public Map<String, Object> accountLogin(LoginDto loginDto) {
        // 效验空值
        if (StrUtil.isBlank(loginDto.getAccount()) || StrUtil.isBlank(loginDto.getCheckPass())) {
            throw new BusinessException("信息不能为空!!!");
        }
        // 账号效验
        LoginInfo loginInfo = checkLogin(loginDto);
        // 效验密码
        if (!DigestUtil.md5Hex(loginInfo.getSalt() + loginDto.getCheckPass()).equals(loginInfo.getPassword())) {
            throw new BusinessException("账号或密码错误!!!");
        }
        if (!loginInfo.getDisable()) {
            throw new BusinessException("该账号被禁用，请联系管理员!!!");
        }
//        生成token，并将登录信息保存到redis数据库，设置30有效
        final Map<String, Object> map = loginSuccessJwtHandler(loginInfo);
        return map;
    }

    // 检测账号密码是否正确
    private LoginInfo checkLogin(LoginDto loginDto) {
        final LambdaQueryWrapper<LoginInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LoginInfo::getPhone, loginDto.getAccount()).or().eq(LoginInfo::getEmail, loginDto.getAccount()).or().eq(LoginInfo::getUsername, loginDto.getAccount()).eq(LoginInfo::getType, loginDto.getType());
        final LoginInfo loginInfo = loginInfoMapper.selectOne(wrapper);
        if (loginInfo == null) {
            throw new BusinessException("账号或密码错误!!!");
        }
        return loginInfo;
    }

    // 微信登录
    @Override
    public Result wechatLogin(String code) {
        /*
        http请求方式: GET
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
         */
        //发送第二次请求 准备参数
        final String url = WxConstants.GET_ACK_URL.replace("APPID", WxConstants.APPID).replace("SECRET", WxConstants.SECRET).replace("CODE", code);
        //发送get请求返回json字符串 返回的结果如下
        /*{
        "access_token":"ACCESS_TOKEN",
        "expires_in":7200,
        "refresh_token":"REFRESH_TOKEN",
        "openid":"OPENID",
        "scope":"SCOPE"
        }*/
        final String jsonStr = HttpUtil.httpGet(url);
        //转换json对象
        final JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        final String access_token = jsonObject.getString("access_token");
        final String openid = jsonObject.getString("openid");
        //通过openid去t_wxuser中查询
        final WxUser wxUser = wxUserMapper.selectOne(new LambdaQueryWrapper<WxUser>().eq(WxUser::getOpenid, openid));
        if (wxUser != null) {//微信已经与账号关联 直接跳转不需要密码登录
            //查找user表的关联信息
            final User user = userMapper.selectById(wxUser.getUserId());
            if (user != null) {
                //查找登录信息
                final LoginInfo loginInfo = loginInfoMapper.selectById(user.getLogininfoId());
                //生成token
           /*     final String token = UUID.randomUUID().toString();
                // 保存信息进入redis
                stringRedisTemplate.opsForValue().set(Constants.LOGIN_TOKEN + token, JSON.toJSONString(loginInfo), 30, TimeUnit.MINUTES);
                //封装返回登录的信息
                Map<String, Object> map = new HashMap<>();
                map.put("token", token);
                map.put("loginInfo", loginInfo);*/
                final Map<String, Object> map = loginSuccessJwtHandler(loginInfo);
                return Result.success(map);
            } else {
                return Result.fail("用户未注册");
            }
        } else {
            // 跳转微信绑定界面 "?accessToken="+accessToken+"&openId="+openId
            //5.没有 - 需要跳转页面绑定 - 响应给前端进行跳转
            Map<String, Object> map = new HashMap<>();
            map.put("accessToken", access_token);
            map.put("openId", openid);
            return Result.fail(map);
        }
    }

    // 微信绑定
    @Override
    public Result wechatBinder(BinderDTO binderDTO) {
        final String phone = binderDTO.getPhone();
        final String verifyCode = binderDTO.getVerifyCode();
        if (StrUtil.isBlank(phone) || StrUtil.isBlank(verifyCode)) {
            throw new BusinessException("数据不能为空");
        }
        final String value = stringRedisTemplate.opsForValue().get(VerifyCodeConstants.PHONE_CODE + phone);
        if (value == null) {
            throw new BusinessException("验证码已经过期！！！");
        }
        //判断验证码输入是否正确
        if (!verifyCode.equals(value.split(":")[0])) {
            throw new BusinessException("验证码错误！！！");
        }
        String url = WxConstants.GET_USER_URL.replace("ACCESS_TOKEN", binderDTO.getAccessToken()).replace("OPENID", binderDTO.getOpenId());
        // 发送获取微信用户信息请求
        final String jsonStr = HttpUtil.httpGet(url);
        WxUser wxUser = jsonStr2WxUser(jsonStr);
        //查询user是否存在
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
        if (user == null) {
            //用户不存在就注册
            user = phone2User(phone);
            // 新增登录对象
            LoginInfo loginInfo = user2LoginInfo(user);
            loginInfoMapper.insert(loginInfo);
            user.setLogininfoId(loginInfo.getId());
            userMapper.insert(user);
        }
        wxUser.setUserId(user.getId());
        wxUserMapper.insert(wxUser);
        // 免密登录
        final LoginInfo loginInfo = loginInfoMapper.selectById(user.getLogininfoId());
        loginInfo.setPassword(null);
        loginInfo.setSalt(null);
        final Map<String, Object> map = loginSuccessJwtHandler(loginInfo);
        return Result.success(map);
    }

    // 用户转换为登录信息
    private LoginInfo user2LoginInfo(User user) {
        final LoginInfo loginInfo = BeanUtil.copyProperties(user, LoginInfo.class);
        loginInfo.setDisable(true);
        return loginInfo;
    }

    // 根据电话号码查询用户
    private User phone2User(String phone) {
        final User user = new User();
        user.setPhone(phone);
        user.setUsername(phone);
        user.setState(1);
        final String salt = RandomUtil.randomString(32);//盐值
        final String pwd = RandomUtil.randomNumbers(6);//随机密码
        user.setSalt(salt);
        user.setPassword(DigestUtil.md5Hex(salt + pwd));//密码
        return user;
    }

    // 获取微信用户对象
    private WxUser jsonStr2WxUser(String jsonStr) {
        final JSONObject res = JSON.parseObject(jsonStr);
        final WxUser wxUser = new WxUser();
        wxUser.setNickname(res.getString("nickname"));
        wxUser.setSex(res.getInteger("sex"));
        wxUser.setOpenid(res.getString("openid"));
        wxUser.setHeadimgurl(res.getString("headimgurl"));
        wxUser.setUnionid(res.getString("unionid"));
        wxUser.setAddress(res.getString("country") + res.getString("province") + res.getString("city"));
        log.info("nickname:{}", res.getString("nickname"));
        log.info("city:{}", res.getString("city"));
        log.info("province:{}", res.getString("province"));
        log.info("headimgurl:{}", res.getString("headimgurl"));
        return wxUser;
    }

    // 对登录成功的用户信息进行jwt加密
    private Map<String, Object> loginSuccessJwtHandler(LoginInfo loginInfo) {
        final HashMap<String, Object> map = new HashMap<>();
        final LoginData loginData = new LoginData();
        // 登录信息
        loginInfo.setSalt(null);
        loginInfo.setPassword(null);
        map.put("loginInfo", loginInfo);
        loginData.setLoginInfo(loginInfo);
        if (loginInfo.getType() == 0) {// 管理员用户
            // 获取登录用户所有权限
            final List<String> permissions = employeeMapper.getPermissionSnByLoginInfoId(loginInfo.getId());
            map.put("permissions", permissions);
            loginData.setPermissions(permissions);
            // 获取登录用户所有菜单
            List<Menu> menus = employeeMapper.getMenus(loginInfo.getId());
            map.put("menus", menus);
            loginData.setMenus(menus);
        }
        try {
            // 生成私钥
            final PrivateKey privateKey = RsaUtils.getPrivateKey(LoginInfoServiceImpl.class.getClassLoader().getResource("auth_rsa.pri").getFile());
            // 使用私钥对登录数据进行加密
            final String token = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 30);
            map.put("token", token);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }
}
